#include<stdio.h>
#include<string.h>
#include<graphics.h>
#include<time.h>
#include<math.h>

#define WIDTH 1024 //屏幕的宽 -> x轴
#define HEIGHT 768 //屏幕的高 -> y轴
#define NUMS_PARTICLE 300 //粒子的数量
#define MAX_SPEED 5 //初始化时最大粒子速度(X或Y轴)
#define COLOR_TYPES 4 //颜色库中的颜色种类
#define MAXLENGTH 999999 //无意义,仅代替大数字
#define CATCH_RADIUS 100 //鼠标粒子捕获半径
#define MAX_FIX 50 //被捕获粒子触边界后的回弹除数
#define DEBUG 1 //是否开启调试模式
#define ATTRACTION 40 //暂时没用
#define LEFTCLICK_HOLDTIME 10000 //在接下来的 LEFTCLICK_HOLDTIME 次执行updateWithoutInput()时不会捕获新的粒子
#define BACKGROUND_COLOR RGB(0,0,0) //背景板颜色
#define NORMAL_LINE_COLOR RGB(50,50,50) //正常连线的颜色
#define CATCHED_LINE_COLOR RGB(120,120,120) //粒子与鼠标连线的颜色
#define PARTICLE_RADIUS 2 //粒子半径
#define ACTUAL_BORDER 100 //实际回弹边界，设置为正时边框变大，为负时边框变小

struct Particle
{
	int x;
	int y;
	int vX;
	int vY;
	int color;
	int near_x; //距离最近的粒子的X轴坐标
	int near_y; //距离最近的粒子的Y轴坐标
	bool isCatch; //是否被捕获
}particle[NUMS_PARTICLE];

int mouse_x, mouse_y;//定义全局变量当前鼠标坐标
int prevMouseX, prevMouseY;//上次鼠标坐标
int mouseVX, mouseVY;//鼠标的速度
int holdTime = 0; //粒子释放后的间歇时间
int remindColorSetting = 0; //DEBUG状态提示颜色设置
int messageHoldTime = 0; //DEBUG状态下单个消息的等待时间

COLORREF findColor(int color) { //根据数字返回对应颜色，COLORREF不理解自行百度定义
	if (color == -1) return RGB(127, 255, 170); //被捕获后的颜色
	if (color == 0) return RGB(200, 255, 255);
	if (color == 1) return RGB(50, 196, 222);
	if (color == 2) return RGB(100, 244, 213);
	if (color == 3) return RGB(255, 182, 193);
	if (DEBUG == 1 && remindColorSetting == 0) {
		printf("未读取到对应颜色，请检查颜色库设置是否正确!\n");
		remindColorSetting = 1; //设置已经提示过了
	}
	return RGB(255, 255, 255); //默认返回白色防止颜色库数量设置有误时程序出错
}

void clearAllCatch() {
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		particle[i].isCatch = false;
		holdTime = LEFTCLICK_HOLDTIME; //在接下来的 LEFTCLICK_HOLDTIME 次执行updateWithoutInput()时不会捕获新的粒子
	}
}

void updateNear() {
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		int minX; int minY; int minDis = MAXLENGTH; //最近粒子的X,Y坐标,最小距离
		for (int j = 0; j < NUMS_PARTICLE; j++) {
			if (i == j) continue; //不比较自己
			int dis = (particle[i].x - particle[j].x) * (particle[i].x - particle[j].x) + (particle[i].y - particle[j].y) * (particle[i].y - particle[j].y);
			if (dis < minDis) {
				minDis = dis;
				minX = particle[j].x; minY = particle[j].y;
			}
		}
		particle[i].near_x = minX; particle[i].near_y = minY;
	}
}

void updateCatch() {
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		//根据粒子与鼠标的距离更新捕获状态
		if ((mouse_x - particle[i].x) * (mouse_x - particle[i].x) + (mouse_y - particle[i].y) * (mouse_y - particle[i].y) < (CATCH_RADIUS * CATCH_RADIUS) + 2000) {
			particle[i].isCatch = true; 
		}
		else {
			particle[i].isCatch = false;
		}
	}
}

void setup()
{
	time_t t;
	srand((unsigned)time(&t)); //随机数发生器
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		particle[i].x = (rand() % (WIDTH + ACTUAL_BORDER + ACTUAL_BORDER)) - ACTUAL_BORDER;
		particle[i].y = (rand() % (HEIGHT + ACTUAL_BORDER + ACTUAL_BORDER)) - ACTUAL_BORDER;
		particle[i].vX = rand() % MAX_SPEED + 1; // +1保证其至少有速度
		if (rand() % 2 == 0) {
			particle[i].vX = -particle[i].vX; // 0.5的概率速度为负
		}
		particle[i].vY = rand() % MAX_SPEED + 1; // +1保证其至少有速度
		if (rand() % 2 == 0) {
			particle[i].vY = -particle[i].vY; // 0.5的概率速度为负
		}
		particle[i].color = rand() % COLOR_TYPES; // 随机分配颜色
	}
	updateNear();
	if (DEBUG) {
		initgraph(WIDTH, HEIGHT, SHOWCONSOLE); //建立窗口
		printf("已开启调试模式\n");
	}
	else {
		initgraph(WIDTH, HEIGHT); //建立窗口
	}

	setbkcolor(BACKGROUND_COLOR); //设置背景色
	cleardevice(); //清屏
}

void show()
{
	cleardevice();
	BeginBatchDraw();
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		if (particle[i].isCatch) {
			setfillcolor(findColor(-1)); //被捕获的小球改颜色
		}
		else {
			setfillcolor(findColor(particle[i].color)); //否则正常读取小球颜色
		}
		solidcircle(particle[i].x, particle[i].y, PARTICLE_RADIUS); //粒子X,Y坐标，半径
		setlinecolor(NORMAL_LINE_COLOR); //设置线的颜色
		line(particle[i].x, particle[i].y, particle[i].near_x, particle[i].near_y);
		if (particle[i].isCatch) { //如果被捕获就增加一条从小球到鼠标的线
			setlinecolor(CATCHED_LINE_COLOR);
			line(particle[i].x, particle[i].y, mouse_x, mouse_y);
		}
	}
	EndBatchDraw();
	FlushBatchDraw();
}

void updateWithoutInput() {
	int count = 0;
	for (int i = 0; i < NUMS_PARTICLE; i++) {
		if (particle[i].isCatch == true && holdTime <= 0) {
			if (DEBUG) count++;
			if ((mouse_x - (particle[i].x + particle[i].vX)) * (mouse_x - (particle[i].x + particle[i].vX)) + (mouse_y - (particle[i].y + particle[i].vY)) * (mouse_y - (particle[i].y + particle[i].vY)) > (CATCH_RADIUS * CATCH_RADIUS)) {
				particle[i].x -= particle[i].vX;
				particle[i].y -= particle[i].vY;
				if ((mouse_x - (particle[i].x + particle[i].vX)) * (mouse_x - (particle[i].x + particle[i].vX)) + (mouse_y - (particle[i].y + particle[i].vY)) * (mouse_y - (particle[i].y + particle[i].vY)) < (CATCH_RADIUS * CATCH_RADIUS) + 3000) {
					particle[i].x += (mouse_x - particle[i].x) / MAX_FIX;
					particle[i].y += (mouse_y - particle[i].y) / MAX_FIX;
				}
			}
		}
		else {
			holdTime--;
			if (holdTime < -10000) holdTime = 0; //防止溢出
		}
		if (particle[i].x + particle[i].vX < 0 - ACTUAL_BORDER || particle[i].x + particle[i].vX > WIDTH + ACTUAL_BORDER || particle[i].y + particle[i].vY < 0 - ACTUAL_BORDER || particle[i].y + particle[i].vY> HEIGHT + ACTUAL_BORDER) {
			if (particle[i].vX > 0) { //碰壁时重新赋予其速度
				particle[i].vX = -((rand() % MAX_SPEED) + 1); // +1保证其至少有速度
			}
			else {
				particle[i].vX = (rand() % MAX_SPEED) + 1; // +1保证其至少有速度
			}
			if (particle[i].vY > 0) { //碰壁时重新赋予其速度
				particle[i].vY = -((rand() % MAX_SPEED) + 1); // +1保证其至少有速度
			}
			else {
				particle[i].vY = (rand() % MAX_SPEED) + 1; // +1保证其至少有速度
			}
		}
		particle[i].x += particle[i].vX; //根据速度更新粒子X轴位置
		particle[i].y += particle[i].vY; //根据速度更新粒子Y轴位置
	}
	if (DEBUG && messageHoldTime <= 0) {
		printf("当前被禁锢粒子数 %d\n", count);
		messageHoldTime = 100;
	}
	else {
		messageHoldTime--;
		if (messageHoldTime < -10000) messageHoldTime = 0; //防止溢出
	}
	updateNear();
}

void updateWithInput()
{
	MOUSEMSG m;//定义鼠标消息
	while (MouseHit())//检测当前是否有鼠标消息
	{
		m = GetMouseMsg();//如果鼠标移动，更新当前鼠标坐标变量
		if (m.uMsg == WM_MOUSEMOVE)//如果鼠标移动，更新当前鼠标坐标变量
		{
			mouse_x = m.x;
			mouse_y = m.y;
		}
		if (m.uMsg == WM_LBUTTONDOWN)//如果左键按下，释放所有被捕获的粒子并添加holdTime
		{
			clearAllCatch();

			int distance = 5;   //移动的像素
			RECT rect;   //RECT结构体，存储窗口的长宽
			HWND window;   //窗口句柄
			int x, y, width, height;   //窗口左上角横、纵坐标；宽度、高度
			int i;//抖动循环次数
			for (i = 0; i < 1; i++)
			{
				window = GetForegroundWindow();   //拿到活动窗口
				GetWindowRect(window, &rect);//通过窗口句柄获得窗口的大小存储在rect结构中
				x = rect.left;//窗口左上角x坐标
				y = rect.top;//窗口左上角y坐标
				width = rect.right - rect.left;//宽度=右坐标减左坐标
				height = rect.bottom - rect.top;//高度=下坐标减上坐标
				MoveWindow(window, x - distance/2.0, y, width, height, TRUE);   //移动窗口;宽度、高度始终为width、height
				MoveWindow(window, x - distance, y -distance, width, height, TRUE);
				MoveWindow(window, x, y - distance, width, height, TRUE);
				MoveWindow(window, x, y, width, height, TRUE);
			}
			if (DEBUG) printf("鼠标左键: x=%d, y=%d\n", mouse_x, mouse_y);
		}
	}
	updateCatch();
}

void over() {

}

int main()
{
	setup();//数据的初始化
	while (1) //死循环
	{
		show();//显示画面
		updateWithoutInput();//与用户输入无关的更新
		updateWithInput();//与用户输入有关的更新
		Sleep(15);
	}
	over();//结束，进行后续处理
	return 0;
}